Skip to content

fix: treat published prerendered HTML as first-class (co-equal priority + publish awaits it)#5517

Merged
habdelra merged 8 commits into
mainfrom
prerender-html-first-class-priority-await
Jul 16, 2026
Merged

fix: treat published prerendered HTML as first-class (co-equal priority + publish awaits it)#5517
habdelra merged 8 commits into
mainfrom
prerender-html-first-class-priority-await

Conversation

@habdelra

@habdelra habdelra commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Background

Stacked on #5511 (dedicated user-index worker tier — fixes createRealm/registration provisioning flakes). This PR fixes the remaining publish-path flake class: host-mode.spec.ts, head-tags.spec.ts, and the publish-realm republish (CS-11043) test.

The issue is semantic. prerender_html was split out of the indexing job into its own priority-9 queue job — one notch below user indexing (10), treated as an off-critical-path follow-on. But for a published realm the rendered HTML is the deliverable: a realm that is indexed (searchable) but not prerendered serves a blank shell. And serve-index.ts reads a card's isolated HTML only from the prerendered_html table with no on-demand fallback, while realm readiness gates on the index pass only — so under load the priority-9 render job sat ~40s behind bigger sweeps and the published URL kept returning a markerless shell past the test's 45s poll.

Changes

1. Prerendered HTML is co-equal with the index (priority 10). User-initiated prerender_html is raised from 9 to 10 — as first-class as the search index. System-initiated prerender-html stays background (0). With prerender-html at 10, the high-tier worker pool serves indexing and rendering as one user-work tier; capacity (not a dedicated lane) keeps both moving.

2. Publishing awaits the HTML (all publish paths). _readiness-check gains an opt-in awaitPrerenderHtml param that, after the index await, waits for the realm's published HTML to be live for its current generation — awaitPublishedHtmlReady polls the prerendered_html artifact for an instance row with isolated_html at ≥ the realm's current generation (gated on generation, so it can't settle on a prior publish's stale rows or race the fire-and-forget enqueue). If the HTML never lands within budget, readiness returns 503 rather than a false 200.

The opt-in is wired through the shared waitForReady operation, so all publish callers await it — the host Publish UI (waitForRealmReady, which backs the "Publishing…" state) and boxel-cli realm publish — not just the matrix helper. Generic readiness (e.g. createRealm, boot) leaves it off, so it stays index-only and fast.

Verification

Stacked on #5511, so CI runs with both. The matrix shards should be reliably green (not a lucky re-run), and the prerender-html queue-wait tail should drop well below the ~45s seen before. createRealm settle-time diagnostics from #5511 should stay low (index not regressed by sharing the tier).

Note

Stacked PR — base is #5511's branch. Retarget to main once #5511 merges. Touches boxel-cli (publish now awaits HTML), hence the fix: title prefix for the version bump.

habdelra and others added 2 commits July 16, 2026 04:36
For a published realm the rendered HTML is the deliverable served to
visitors — a realm that is indexed but not prerendered serves a blank
shell — so user-initiated prerendered HTML is as first-class as the search
index, not an off-critical-path follow-on. Raise userInitiatedPrerenderHtml
from 9 to 10, co-equal with user indexing; system-initiated prerender-html
stays background (0).

With prerender-html at 10 the high-priority worker pool (which floors at
that tier) and the user-index pool both floor at 10 and serve all
user-initiated work — indexing and rendering alike. What keeps each moving
is worker capacity, not a dedicated lane; the split across the two count
knobs is now immaterial. Comments on both the worker-manager tiers and the
matrix harness updated to the co-equal model.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A published realm's rendered HTML is its deliverable, but prerender-html
runs on a separate fire-and-forget channel after the index pass, so a realm
can report ready (indexed) while still serving a shell without card markup —
the source of the host-mode / head-tags publish flakes.

Make publishing await the HTML. `_readiness-check` gains an opt-in
`awaitPrerenderHtml` param that, after the index await, also waits for the
realm's published HTML to be live for its current generation
(`awaitPublishedHtmlReady`: polls `prerendered_html` for an instance row with
isolated_html at >= the realm's current generation — the artifact, gated on
generation, so it can't settle on a prior publish's stale rows or race the
fire-and-forget enqueue). The publish handler points its 202 `Location` at the
flagged readiness URL, and the matrix `publishRealm` helper waits on it, so a
publish is not "done" until the realm is both indexed and rendered. Default
readiness stays index-only, so createRealm / boot readiness are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: addba793f9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/realm-server/handlers/handle-publish-realm.ts
Comment thread packages/runtime-common/realm.ts Outdated
…when HTML never lands

Two gaps in the prior commit:

1. Only the 202 Location carried awaitPrerenderHtml, so the shared waitForReady
   operation — used by the host Publish UI and boxel-cli — still polled the
   index-only readiness URL and reported a publish complete once the index
   settled, while the render was still queued. waitForReady gains an opt-in
   awaitPrerenderHtml flag; the two publish callers (host waitForRealmReady,
   boxel-cli realm publish) set it, so "publish awaits HTML" reaches production,
   not just the matrix test. Generic readiness (createRealm) leaves it off.

2. readinessCheck ignored awaitPublishedHtmlReady's result, so a render failure
   or a wait past its budget still returned 200. Return 503 (Retry-After) when
   the HTML isn't live for the current generation, so pollers keep waiting and a
   single-shot caller sees the failure. The matrix publishRealm helper now polls
   the readiness URL until 200 instead of a single blocking GET.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@habdelra habdelra changed the title Treat published prerendered HTML as first-class: co-equal priority + publish awaits it fix: treat published prerendered HTML as first-class (co-equal priority + publish awaits it) Jul 16, 2026
@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Preview deployments

Host Test Results

    1 files      1 suites   3h 7m 49s ⏱️
3 535 tests 3 520 ✅ 15 💤 0 ❌
3 554 runs  3 539 ✅ 15 💤 0 ❌

Results for commit 684125e.

Realm Server Test Results

    1 files      1 suites   16m 1s ⏱️
1 845 tests 1 845 ✅ 0 💤 0 ❌
1 924 runs  1 924 ✅ 0 💤 0 ❌

Results for commit 684125e.

habdelra and others added 2 commits July 16, 2026 05:14
…a successful isolated_html row

awaitPublishedHtmlReady required an instance row with non-null isolated_html
at the current generation, which never appears for a realm whose renders
error (e.g. the boxel-cli integration harness's noop prerenderer, where every
instance becomes an error document) — so readiness hung and publish timed out.

Gate instead on any prerendered_html row at >= the current generation:
batch.done() swaps a generation's rows in atomically, so a row at the current
generation means that generation's render batch has landed, whether the
renders succeeded (isolated HTML present) or errored (error rows). Either way
the publish's async render work is done; a genuine render failure surfaces
downstream as missing markup rather than hanging readiness.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 202 Location now points at the readiness check gated on the published HTML
being rendered (?awaitPrerenderHtml=true), so a publish poll only reports ready
once the realm is both indexed and viewable. Update the expected Location.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes published prerendered HTML a first-class artifact in the publish flow by (1) elevating user-initiated prerender_html work to the same priority tier as user indexing and (2) extending readiness/publish waiting so “publish complete” can be gated on rendered HTML being available for the current realm generation.

Changes:

  • Raise user-initiated prerender_html queue priority to 10 (co-equal with user indexing) and update worker-tier documentation accordingly.
  • Add an opt-in readiness gate (awaitPrerenderHtml=true) and plumb it through the shared waitForReady operation; update publish callers (host UI + boxel-cli) to use it.
  • Update publish endpoint Location header and relevant tests/helpers to poll readiness rather than racing the served published URL.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/runtime-common/realm.ts Adds awaitPrerenderHtml query-param gating to _readiness-check, returning 503 while published HTML isn’t ready.
packages/runtime-common/realm-operations.ts Extends waitForReady with awaitPrerenderHtml to append the query param when polling readiness.
packages/runtime-common/queue.ts Makes user-initiated prerender-html priority co-equal with user jobs (10).
packages/runtime-common/jobs/prerender-html.ts Updates priority commentary and introduces awaitPublishedHtmlReady polling helper.
packages/realm-server/worker-manager.ts Updates tier semantics commentary for worker pools given priority changes.
packages/realm-server/handlers/handle-publish-realm.ts Updates publish 202 Location URL to include awaitPrerenderHtml=true.
packages/realm-server/tests/publish-unpublish-realm-test.ts Updates expectation for Location header and clarifies that readiness is gated on rendered HTML.
packages/matrix/tests/host-mode.spec.ts Waits for published realm readiness (indexed + rendered) via readiness-check instead of racing the served URL.
packages/matrix/support/isolated-realm-server.ts Updates worker-tier explanation for the matrix isolated stack.
packages/host/app/services/realm-server.ts Publish UI now waits for readiness gated on prerendered HTML.
packages/boxel-cli/src/commands/realm/publish.ts CLI publish now waits for readiness gated on prerendered HTML.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/runtime-common/jobs/prerender-html.ts
Comment thread packages/runtime-common/jobs/prerender-html.ts Outdated
habdelra and others added 3 commits July 16, 2026 07:43
Readiness callers already re-poll at ~1s (Retry-After: 1) and HTML rendering
takes seconds, so the 250ms internal interval only multiplied DB queries
(~240 per blocking request) under concurrent publish polls without improving
latency. Default to 1000ms.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Responsiveness wins over the modest query volume here: the probe is a single
indexed `SELECT 1 … LIMIT 1`, and readiness should return promptly once the
render batch lands rather than tracking the caller's coarser ~1s re-poll.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the fixed-interval poll with an event-driven wait: pg-queue emits
NOTIFY jobs_finished when a job's finalize transaction commits, and the
prerender-html batch's generation swap is durable by then, so re-checking on
that signal catches the current generation landing near-instantly — with far
fewer DB queries than a 250ms poll under concurrent publish polls.

The LISTEN is feature-detected (PgAdapter exposes subscribe; SQLite has no
pub/sub) and fire-and-forget: a coarse periodic poll remains as the guarantee,
covering a missed notification, a failed/slow LISTEN, and adapters without
pub/sub, so readiness never hangs on the subscription. Resolves as soon as the
render channel catches up, false on timeout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@habdelra
habdelra requested a review from a team July 16, 2026 12:05
@habdelra
habdelra changed the base branch from fix-flaky-publish-realm-createrealm-worker to main July 16, 2026 13:19
@habdelra
habdelra merged commit b898aa7 into main Jul 16, 2026
77 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants